declare var Prism: any;
declare var asyncRequest: any;
declare var importCss: any;
/**动画的参数 */
interface IAnimateConfig {
    /**起始值 */
    from: number;
    /**终值 */
    to: number;
    /**延时  s*/
    delay?: number;
    /**持续时间  s*/
    duration?: number;
    /**动画效果 */
    timingFunction?: string; //linear|easeIn|easeOut|easeInOut
}
/**向XBRLViewer请求相应位置时的参数 */
interface IXBRLQuery {
    /**标签的命名空间 */
    namespace?: string;
    /**标签 */
    tagname: string;
    /**其它属性 */
    attrs?: any
}
/**XBRL字符串解析时，生成开半标签时的钩子 */
/**
 * @param el :当前钩子所执行的节点元素
 * @param str ：前一钩子已经生成的字符串
 * @return 钩子需要返回字符串
 */
type IHookFunction = (el: Element, str?: string|Element) => string|void;
interface IHook {
    /**开始标签钩子 */
    openHook: IHookFunction[];
    /**子节点钩子 */
    childHook: IHookFunction[];
    /**闭合标签钩子 */
    closeHook: IHookFunction[];
}
class XBRLCore {
    xbrl: string = "";
    xbrlHtml: string;
    /**生成钩子 */
    hook: IHook = { openHook: [], childHook: [], closeHook: [] };

    hookDom: IHook = { openHook: [], childHook: [], closeHook: [] };
    /**iframe内的变量 */
    $window: Window = null;
    $document: Document = null;
    /**常量 */
    readonly LT = "&lt;";
    readonly GT = "&gt;";
    readonly SP = "&nbsp;";

    constructor(public el: HTMLIFrameElement, public cssPath: string) {
        this.el.setAttribute("allowFullscreen", "true");
        this.$window = this.el.contentWindow;
        this.$document = this.el.contentDocument// || this.el.document;

    }
    $(selector: string): NodeList { return this.$document.querySelectorAll(selector) }
    $$(selector: string): Array<HTMLElement> { return Array.prototype.slice.call(this.$(selector)) }
    matches(el, selector) {
        if (!selector || !el || el.nodeType !== 1) return false
        var _matches = (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector);
        return _matches.call(el, selector);
    }
    //动画计算
    /**
     * 
     * @param config 
     * @param action 
     */
    animate(config: IAnimateConfig, action: (value: number) => void) {
        var win = this.$window, scrope = this;
        var opt: IAnimateConfig = { from: 0, to: 0, delay: 0, duration: 1, timingFunction: 'linear' };
        for (var p in config) opt[p] = config[p];

        if (opt.from == opt.to) return;
        var begin = +new Date() + opt.delay * 1000, end = begin + opt.duration * 1000, last = +new Date();
        var tick = function () {
            if (last >= begin && last <= end) {
                var v = (function (start, alter, curTime, dur) {
                    var progress = curTime / dur * 2;
                    return (progress < 1 ? Math.pow(progress, 2) : -((--progress) * (progress - 2) - 1)) * alter / 2 + start;
                })(opt.from, opt.to - opt.from, last - begin, end - begin);
                action(v);
            }
            if (last > end) {
                action(opt.to); return;
            }
            last = +new Date();
            win.requestAnimationFrame(tick);
        };
        tick();
    }

    /**从url加载数据 */
    load(ajaxParameters) {
        var scrope = this;
        asyncRequest(ajaxParameters, function (xbrl) {
            scrope.render(xbrl.response);
        }, this, function (xhr) {
            this.onError && this.onError(`请求数据错误：${xhr.statusText}`);
        })
    }
    /**刷新显示*/
    render(xbrl: string) {
        this.xbrl = xbrl;
        try {
            var parser = new DOMParser();
            var root = parser.parseFromString(this.xbrl, "application/xml");
            root.children
            //IE11中，root是XMLDocument对象，没有  children属性
            if (!root.children) {
                Object.defineProperty(XMLDocument.prototype, "children", {
                    get: function () {
                        var nodes = Array.prototype.slice.call(this.childNodes) as Node[];
                        return nodes.filter((n) => n.nodeType == 1);
                    }
                });
                Object.defineProperty(Element.prototype, "children", {
                    get: function () {
                        var nodes = Array.prototype.slice.call(this.childNodes) as Node[];
                        return nodes.filter((n) => n.nodeType == 1);
                    }
                });
            }
            importCss(this.$document, this.cssPath)
            console.time("1")
            this.xbrlHtml = this.renderElement(root.children[0]);
            this.$document.body.innerHTML = this.xbrlHtml;
            // var blockEl = this.renderElementbyDom(root.children[0]);
            // this.$document.body.appendChild(blockEl)
            console.timeEnd("1")

        } catch (e) {
            console.log(`xbrl parse error：${this.xbrl}`);
        }

    }
    renderElement(el: Element): string {
        return this.runHook(this.hook.openHook, el) +
            this.runHook(this.hook.childHook, el) +
            this.runHook(this.hook.closeHook, el);
    }
    //按钩子注册顺序执行，并返回执行后的总结果
    runHook(hook, el): string {
        var scope = this, str = "";
        return hook.reduce(function (p, c) {
            return c.call(scope, el, p);
        }, "");
    }
    createDom(config: any) {
        var el = document.createElement(config.tag);
        el.className = (config.clsName || "");
        el.innerText = config.text;
        return el;
    }
    renderElementbyDom(el: Element): Element {
        var blockEl = this.createDom({ tag: "div", clsName: "xmlBlock", text: "" });
        this.runHookbyDom(this.hookDom.openHook, el, blockEl)
        this.runHookbyDom(this.hookDom.childHook, el, blockEl)
        this.runHookbyDom(this.hookDom.closeHook, el, blockEl)
        return blockEl;
    }
    //按钩子注册顺序执行，并返回执行后的总结果
    runHookbyDom(hook: any[], el: Element, blockEl: Element): Element {
        var scope = this;
        hook.forEach(function (h) {
            blockEl = h.call(scope, el, blockEl);
        })
        return blockEl;
    }

    on(eventName: string, handler: any) {
        this.$document.addEventListener(eventName, handler, false);
    }
    off(eventName: string, handler: any) {
        this.$document.removeEventListener(eventName, handler, false);
    }
    bindEventOnClsName(eventName: string, listenCls: string[]) {
        var doc = this.$document;
        doc.addEventListener(eventName, function (event) {
            var target = event.target as Element;
            while (target != doc.body) {
                for (var i = 0; i < listenCls.length; i++) {
                    if (target.classList && target.classList.contains(listenCls[i])) {
                        var bubble = true, ev = null;
                        try {
                            ev = new CustomEvent(listenCls[i] + eventName, {
                                detail: target, bubbles: false, cancelable: true
                            });
                        } catch (ex) {
                            //IE11不支持 new CustomEvent()
                            ev = document.createEvent('CustomEvent');
                            ev.initCustomEvent(listenCls[i] + eventName, true, true, target);
                        }
                        ev && (bubble = doc.dispatchEvent(ev));
                        //如果不再冒泡，则立即返回
                        if (!bubble) return;
                    }
                }
                target = target.parentElement;
            }
        }, false);
    }
}
/**xbrl脚本在网页高亮显示的类   by shenjunjian */
class XBRLViewer extends XBRLCore {
    private $lastSelected: HTMLElement[] = [];
    constructor(public el: HTMLIFrameElement, public cssPath: string) {
        super(el, cssPath);
        this.hook.openHook.push(this.hOpen);
        this.hook.childHook.push(this.hChild);
        this.hook.closeHook.push(this.hClose);

        this.hookDom.openHook.push(this.hOpenbyDom)
        this.hookDom.childHook.push(this.hChildbyDom)
        this.hookDom.closeHook.push(this.hClosebyDom)

        this.bindEventOnClsName("click", ["xmlBlock", "attr", "nodeName", "textValue", "plus"]);
        this.on("plusclick", this.fPlusClick)
    }

    /**默认的钩子函数 */
    //生成开始标签，但不包含最后那个  > 括号。
    private hOpen(el: Element, str: string): string {
        var str = `<div class="xmlBlock">`;
        el.children.length && (str += `<span class="plus"></span><span class="mark"></span>`);
        str += `<span class="nodeName">${this.LT}${el.nodeName}</span>`;

        if (el.attributes.length) {
            for (var i = 0; i < el.attributes.length; i++) {
                var attr = el.attributes[i];
                str += `<span class="attr">${this.SP}
                            <span class="attrName">${attr.nodeName}</span>
                            <span class="attrValue">"${attr.nodeValue}</span>"
                        </span>`;
            }
        }
        return str;
    }
    private hChild(el: Element, str: string): string {
        var str = `<span class="nodeName">${this.GT}</span>`;
        //叶子节点
        if (!el.children.length) {
            //自闭合, 什么也不生成
            if (el.textContent == "") {
                str = "";
            } else { //文本节点
                str += `<span class="textValue">${el.textContent}</span>`;
            }
        } else {
            for (var i = 0; i < el.children.length; i++) {
                str += this.renderElement(el.children[i]);
            }
        }
        return str;
    }
    private hClose(el: Element, str: string): string {
        var len = el.childNodes.length;
        if (!len) return `<span class="nodeName">${this.SP}/${this.GT}</span></div>`;
        else return `<span class="nodeName">${this.LT}/${el.nodeName}${this.GT}</span></div>`
    }


    private hOpenbyDom(el: Element, blockEl?: Element) {
        el.children.length && blockEl.appendChild(this.createDom({ tag: 'span', clsName: "plus", text: "" }));
        blockEl.appendChild(this.createDom({ tag: "span", clsName: "nodeName", text: "<" + el.nodeName }))
        if (el.attributes.length) {
            for (var i = 0; i < el.attributes.length; i++) {
                var attr = el.attributes[i];
                var attrDom = this.createDom({ tag: "span", clsName: "attr", text: "" });
                attrDom.appendChild(this.createDom({ tag: "span", clsName: "attrName", text: attr.nodeName }))
                attrDom.appendChild(this.createDom({ tag: "span", clsName: "attrValue", text: `"${attr.nodeValue}"` }))
                blockEl.appendChild(attrDom);
            }
        }
    }
    private hChildbyDom(el: Element, blockEl?: Element) {
        var gtTag = this.createDom({ tag: "span", clsName: "nodeName", text: ">" })
        //叶子节点
        if (!el.children.length) {
            //自闭合, 什么也不生成
            if (el.textContent !== "") {
                blockEl.appendChild(gtTag);
                blockEl.appendChild(this.createDom({ tag: "span", clsName: "textValue", text: el.textContent }))
            }
        } else {
            blockEl.appendChild(gtTag);
            for (var i = 0; i < el.children.length; i++) {
                blockEl.appendChild(this.renderElementbyDom(el.children[i]));
            }
        }
    }
    private hClosebyDom(el: Element, blockEl?: Element) {
        var len = el.childNodes.length;
        if (!len) blockEl.appendChild(this.createDom({ tag: "span", clsName: "nodeName", text: "/>" }))
        else blockEl.appendChild(this.createDom({ tag: "span", clsName: "nodeName", text: `</${el.nodeName}>` }))
    }
    /**折叠点击事件 */
    fPlusClick(ev) {
        var plus = ev.detail as Element;
        plus.parentElement.classList.toggle("fold");
    }
}

class XBRLEditer extends XBRLViewer {
    constructor(public el: HTMLIFrameElement, public cssPath: string) {
        super(el, cssPath);
        this.hook.openHook.push(this.hOpenDelPlus)
        this.hook.childHook.push(this.hChildWithEditor);
        this.off("plusclick", this.fPlusClick)
    }
    private hOpenDelPlus(el: Element, str: string): string {
        el.children.length && (str = str.replace(`<span class="plus"></span>`, ""));
        return str;
    }
    //当叶子节点，且有内容时，把内容放到input中去！
    private hChildWithEditor(el: Element, str: string): string {
        if (!el.children.length && el.textContent != "") {
            return str.replace(/<span class="textValue">(.*?)<\/span>/g,
                function (a, b) {
                    return `<span class="textValue"><input type="text" value="${b}" /><\/span>`
                })
        } else {
            return str;
        }
    }
}

    // /**选择高亮文本 */
    // docSelect(begin, end) {
    //     var range = this.$document.createRange();
    //     range.setStart(begin, 0);
    //     range.setEnd(end, end.childElementCount);
    //     var selection = this.$window.getSelection();
    //     selection.removeAllRanges();
    //     selection.addRange(range);
    //     this.$window.focus();
    // }
//   /** 从页面中，查找请求位置的第一个元素 。 */
//     private findInHtml(q: IXBRLQuery): HTMLElement {
//         var scrope = this;
//         var s_tag = "<" + (q.namespace ? q.namespace + ':' : '') + q.tagname;
//         var $tag: HTMLElement = null;
//         //1、根据命名空间+标签，先查找出可能的  <span class="token tag"> 元素
//         var nodeNames = this.$$(".nodeName")
//             .filter(function (t) {
//                 return t.innerText == s_tag;
//             });
//         if (!!nodeNames.length) {
//             //2、没有属性条件，则直接返回已经找到的元素
//             if (typeof q.attrs == 'undefined' || !Object.keys(q.attrs)) { $tag = nodeNames[0]; }
//             else {
//                 //3、有属性条件，则遍历每一个$spans，判断它的属性是否满足
//                 //每个标签的属性都是由这三个组成   attr-name   attr-value ="xxx" .....  punctuation
//                 for (var i = 0; i < nodeNames.length; i++) {
//                     var attrTags = nodeNames[i].parentElement.querySelectorAll(".attr");  //所有属性标签
//                     if (attrTags.length<Object.keys(q.attrs).length) continue;

//                     var flag = true;  //是否满足所有属性。初始时假设满足
//                     for (var attr in q.attrs) {

//                     }
//                     //属性条件满足，则添加到$tag中去。
//                     if (flag) {
//                         $tag = nodeNames[i].parentElement;
//                         break;
//                     }
//                 }
//             }
//         }

//         return $tag;
//     }
//     /**从字符串中，查找请求的第一个位置。*/
//     private findInXML(q: IXBRLQuery): string {
//         //不考虑自闭合标签，它们不需要修改值的。 不考虑多个匹配的情况，因为修改时，只对应第一个值
//         var s_tag = "<" + (q.namespace ? q.namespace + ':' : '') + q.tagname;
//         var e_tag = "</" + (q.namespace ? q.namespace + ':' : '') + q.tagname + ">";
//         var r = new RegExp(s_tag + "[\\s\\S]*?" + e_tag, "gmi");
//         var matchs = this.xbrl.match(r);

//         //找不到，则返回空
//         if (!matchs) return "";

//         //找到，且无属性条件，直接返回找到位置
//         if (typeof q.attrs == 'undefined' || !Object.keys(q.attrs)) return matchs[0];
//         //判断属性条件
//         var attrList = [];
//         for (var attr in q.attrs) {
//             attrList.push(attr + '="' + q.attrs[attr] + '"');
//         }
//         for (var i = 0; i < matchs.length; i++) {
//             var flag = true;
//             for (var j = 0; j < attrList.length; j++) {
//                 flag = flag && matchs[i].indexOf(attrList[j]) > -1
//             }
//             if (flag) return matchs[i];
//         }
//         return '';
//     }

//     /**选中指标值时，滚动到指定的xbrl显示位置，并高亮显示 */
//     public highLight(q: IXBRLQuery, value?: string): boolean {
//         var win = this.$window;
//         //高亮并滚动显示
//         //去掉样式
//         this.$lastSelected.forEach(function (tag) {
//             tag.classList.remove("xbrl-selected");
//         })
//         this.$lastSelected.length = 0;
//         var tags = this.findInHtml(q);
//         if (!tags) return false;
//         var begin = tags[0], end = tags[1];
//         //为元素添加上样式,并记录到last
//         begin.classList.add("xbrl-selected-first");
//         var save = (el: HTMLElement) => {
//             this.$lastSelected.push(el);
//             el.classList.add("xbrl-selected");
//         }
//         save(begin);

//         if (end != begin) {
//             var curElement = begin.nextElementSibling as HTMLElement;
//             while (curElement && curElement != end) {
//                 save(curElement);
//                 curElement = curElement.nextElementSibling as HTMLElement;
//             }
//             save(end);
//         }
//         this.animate({
//             from: this.$window.scrollY || this.$window.pageYOffset || this.$document.documentElement.scrollTop,
//             to: Math.min(begin.offsetTop, this.$document.body.clientHeight - this.$document.documentElement.clientHeight),
//             delay: 0.2, duration: 1,
//             timingFunction: 'easeInOut'
//         }, (v) => win.scrollTo(0, v));

//         //更新值
//         if (typeof value !== 'undefined') {
//             if (begin.nextSibling && begin.nextSibling.nodeType == 3) {
//                 begin.nextSibling.textContent = value;

//                 //xbrl替換
//                 var oldStr = this.findInXML(q);
//                 var r = new RegExp(">([\\s\\S]*?)<", "gmi");
//                 var newStr = oldStr.replace(r, `>${value}<`);
//                 this.xbrl = this.xbrl.replace(oldStr, newStr);
//             } else {
//                 //没有文字节点
//                 return false;
//             }
//         }
//         return true;
//     }